//--------------------------------------------------------------------------------------------------
//                                                   ___  __  __  __  __  __  __  __    __  __  ___ 
// Copyright 2010, Koert van der Veer.              / __)(  )(  )(  \/  )(  )(  )(  )  (  )(  )/ __)
// Licensed under the Apache License,              ( (__  )(__)(  )    (  )(__)(  )(__  )(__)( \__ \
// version 2.0; ("the License")                     \___)(______)(_/\/\_)(______)(____)(______)(___/

// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at 
//     http://www.apache.org/licenses/LICENSE-2.0 
//
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS,  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions and limitations under
// the License. 
//--------------------------------------------------------------------------------------------------
// This file is part of cumulus,  an memory manager implementation in C++.  Details are discussed in
// my blog,  which can be found at http://ondergetekende.nl.  Comments,  suggestions and bug reports
// are welcome. They can be added as comments at the blog. Be advised that in its current form, this
// code is most likely not production ready; it simply hasn't been tested sufficiently. 
//--------------------------------------------------------------------------------------------------

#pragma once

#include <windows.h>

namespace Cumulus
{
	template< typename InnerLayer >
	class CriticalSectionLock : public InnerLayer
	{
	public:
		static const bool IsThreadSafe = true;

	private:
		CRITICAL_SECTION CrititalSection;

		struct ScopeLock : public NonAssignable
		{
			ScopeLock(CRITICAL_SECTION* InCrititalSection) 
				:CrititalSection( InCrititalSection )
			{
				EnterCriticalSection(CrititalSection); 
			}

			~ScopeLock() 
			{ 
				LeaveCriticalSection(CrititalSection); 
			}

		private:
			CRITICAL_SECTION* CrititalSection;
		};

	public:
		CriticalSectionLock()
		{
			InitializeCriticalSection(&CrititalSection);
		}

		~CriticalSectionLock()
		{
			DeleteCriticalSection(&CrititalSection);
		}

		void* Malloc( size_t& Size ) 
		{
			ScopeLock Lock( CrititalSection );
			return InnerLayer::Malloc( Size ); 
		}

		void Free( void* Memory ) 
		{
			ScopeLock Lock( CrititalSection );
			InnerLayer::Free( Memory ); 
		}

		size_t GetSize( void* Memory )
		{
			ScopeLock Lock( CrititalSection );
			return InnerLayer::GetSize( Memory ); 
		}

		bool Resize( void* Memory, size_t& NewSize )
		{
			ScopeLock Lock( CrititalSection );
			return InnerLayer::Resize( Memory, NewSize ); 
		}

		void* GetNext( void* Memory )
		{
			ScopeLock Lock( CrititalSection );
			return InnerLayer::GetNext( Memory ); 
		}

		void* GetPrevious( void* Memory )
		{
			ScopeLock Lock( CrititalSection );
			return InnerLayer::GetPrevious( Memory ); 
		}
	};

	template< typename InnerLayer >
	class MutexLock : public InnerLayer
	{
	public:
		static const bool IsThreadSafe = true;

	private:
		HANDLE hMutex;

		struct ScopeLock : public NonAssignable
		{
			ScopeLock( HANDLE hInMutex ) 
				: hMutex( hInMutex )
			{
				DWORD WaitResult = WaitForSingleObject( 
					hMutex,    // handle to mutex
					INFINITE);  // no time-out interval

				assert( WaitResult == WAIT_OBJECT_0 );
			}

			~ScopeLock() 
			{ 
				ReleaseMutex(hMutex); 
			}

		private:
			HANDLE hMutex;
		};

	public:
		MutexLock()
		{
			hMutex = CreateMutex( 
				NULL,              // default security attributes
				FALSE,             // initially not owned
				NULL);
		}

		~MutexLock()
		{
			CloseHandle(hMutex);
		}

		void* Malloc( size_t& Size ) 
		{
			ScopeLock Lock( hMutex );
			return InnerLayer::Malloc( Size ); 
		}

		void Free( void* Memory ) 
		{
			ScopeLock Lock( hMutex );
			InnerLayer::Free( Memory ); 
		}

		size_t GetSize( void* Memory )
		{
			ScopeLock Lock( hMutex );
			return InnerLayer::GetSize( Memory ); 
		}

		bool Resize( void* Memory, size_t& NewSize )
		{
			ScopeLock Lock( hMutex );
			return InnerLayer::Resize( Memory, NewSize ); 
		}

		void* GetNext( void* Memory )
		{
			ScopeLock Lock( hMutex );
			return InnerLayer::GetNext( Memory ); 
		}

		void* GetPrevious( void* Memory )
		{
			ScopeLock Lock( hMutex );
			return InnerLayer::GetPrevious( Memory ); 
		}
	};
}